/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <isa.h>
#include <cpu/cpu.h>
#include <difftest-def.h>
#include <memory/paddr.h>


typedef struct __DiffContext__ {
	uint64_t* gpr;
	uint64_t pc;
	riscv64_CSRs* csr;
} DiffContext;

static void diff_get_regs(void* dut) {
	DiffContext* ctx = (DiffContext*)dut;
	for (int i = 0; i < 32; ++i) {
		ctx->gpr[i] = cpu.gpr[i];
	}
	ctx->pc = cpu.pc;
	ctx->csr->mstatus = cpu.csr.mstatus;
	ctx->csr->mie = cpu.csr.mie;
	ctx->csr->mtvec = cpu.csr.mtvec;
	ctx->csr->mepc = cpu.csr.mepc;
	ctx->csr->mcause = cpu.csr.mcause;
	// ctx->csr.mip = cpu.csr.mip;
}

static void diff_set_regs(void* diff_context) {
	DiffContext* ctx = (DiffContext*)diff_context;
	for (int i = 0; i < 32; ++i) {
		cpu.gpr[i] = ctx->gpr[i];
	}
	cpu.pc = ctx->pc;
	cpu.csr.mstatus = ctx->csr->mstatus;
	cpu.csr.mie = ctx->csr->mie;
	cpu.csr.mtvec = ctx->csr->mtvec;
	cpu.csr.mepc = ctx->csr->mepc;
	cpu.csr.mcause = ctx->csr->mcause;
	// cpu.csr.mip = ctx->csr.mip;
}

__EXPORT void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) {
	if (direction == DIFFTEST_TO_REF) {
		for (int i = 0; i < n; ++i) {
			paddr_write(addr + i, 1, *((uint8_t*)buf+i));
		}
	} else {
		assert(0);
	}
}

__EXPORT void difftest_regcpy(void *dut, bool direction) {
	if (direction == DIFFTEST_TO_REF) {
		diff_set_regs(dut);
	} else {
		diff_get_regs(dut);
	}
}

__EXPORT void difftest_exec(uint64_t n) {
	cpu_exec(n);
}

__EXPORT void difftest_raise_intr(word_t NO) {
	cpu.pc = isa_raise_intr(NO, cpu.pc);
}

__EXPORT void difftest_init(int port) {
	void init_mem();
	init_mem();
	/* Perform ISA dependent initialization. */
	init_isa();
}
